package com.ligq.h265toupinpush.toupin

import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.media.MediaCodec
import android.media.MediaCodecInfo
import android.media.MediaFormat
import android.media.projection.MediaProjection
import android.util.Log
import com.ligq.common.constants.FrameType
import java.nio.ByteBuffer

const val TAG = "ligq"

/**
 *  encodeType:0表示h264；其他表示h265
 */
class CodeLiveH26X(
    private val encodeType: Int,
    mediaProjection: MediaProjection,
    private val socketLive: SocketLive
) : Thread() {
    private lateinit var mediaCodec: MediaCodec
    private lateinit var virtualDisplay: VirtualDisplay
    private lateinit var configFrameBuf: ByteArray
    private val width = 1080
    private val height = 2160

    init {
        val type =
            if (encodeType == 0) MediaFormat.MIMETYPE_VIDEO_AVC else MediaFormat.MIMETYPE_VIDEO_HEVC
        try {
            val format =
                MediaFormat.createVideoFormat(type, width, height)
            format.setInteger(MediaFormat.KEY_FRAME_RATE, 30)
            format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10)
            format.setInteger(MediaFormat.KEY_BIT_RATE, width * height)
            format.setInteger(
                MediaFormat.KEY_COLOR_FORMAT,
                MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface
            )
            mediaCodec = MediaCodec.createEncoderByType(type)
            mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
            val surface = mediaCodec.createInputSurface()
            virtualDisplay = mediaProjection.createVirtualDisplay(
                "sr_live", width, height,
                3, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, surface, null, null
            )
        } catch (e: Exception) {
            Log.e(TAG, ": ", e)
        }
    }

    override fun run() {
        super.run()
        mediaCodec.start()
        val bufferInfo = MediaCodec.BufferInfo()
        while (true) {
            val outIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 10000)
            if (outIndex >= 0) {
                val outputBuffer = mediaCodec.getOutputBuffer(outIndex)
                dealFrame(outputBuffer!!, bufferInfo)
//                FileUtils.writeBytes(FileUtils.NAME_H265, outData)
                mediaCodec.releaseOutputBuffer(outIndex, false)
            }
        }
    }

    private fun dealFrame(outData: ByteBuffer, bufferInfo: MediaCodec.BufferInfo) {
        var offset = 4
        if (outData.get(2).toInt() == 0x01) {
            offset = 3
        }
        if (encodeType == 0) {
            //sps数据，需要存储
            when (outData.get(offset).toInt() and 0x1F) {
                FrameType.TYPE_H264_SPS.frameType -> {
                    configFrameBuf = ByteArray(bufferInfo.size)
                    outData.get(configFrameBuf)
                }
                FrameType.TYPE_H264_I.frameType -> {
                    val dataIframe = combineIData(bufferInfo, outData)
                    //发送数据 dataIframe
                    socketLive.sendData(dataIframe)
                }
                else -> {
                    val nIData = ByteArray(bufferInfo.size)
                    outData.get(nIData)
                    //发送数据 nIData
                    socketLive.sendData(nIData)
                    Log.d(TAG, "dealFrame: H265非I帧data---${nIData.contentToString()}")
                }
            }
        } else {
            when ((outData.get(offset).toInt() and 0x7F) shr 1) {
                FrameType.TYE_H265_VPS.frameType -> {
                    configFrameBuf = ByteArray(bufferInfo.size)
                    outData.get(configFrameBuf)
                }
                FrameType.TYE_H265_I.frameType -> {
                    val dataIframe = combineIData(bufferInfo, outData)
                    //发送dataIframe数据
                    socketLive.sendData(dataIframe)
                }
                else -> {
                    val nIData = ByteArray(bufferInfo.size)
                    outData.get(nIData)
                    //发送数据 nIData
                    socketLive.sendData(nIData)
                    Log.d(TAG, "dealFrame: H265非I帧data---${nIData.contentToString()}")
                }
            }
        }
    }

    private fun combineIData(
        bufferInfo: MediaCodec.BufferInfo,
        outData: ByteBuffer
    ): ByteArray {
        val iData = ByteArray(bufferInfo.size)
        outData.get(iData)
        val newData = ByteArray(bufferInfo.size + configFrameBuf.size)
        System.arraycopy(configFrameBuf, 0, newData, 0, configFrameBuf.size)
        System.arraycopy(iData, 0, newData, configFrameBuf.size, iData.size)
        return newData
    }

}